﻿#include "frmmain.h"
#include "iconhelper.h"
#include "quihelper.h"
#include "ui_frmmain.h"

#include <QAudioInput>
#include <QMediaFormat>

// https://doc.qt.io/qt-6.5/cameraoverview.html#camera-image-processing

frmMain::frmMain(QWidget *parent) : QDialog(parent), ui(new Ui::frmMain) {
    ui->setupUi(this);
    this->initForm();
    this->initResizeMembers();
    this->loadDevices();

    recording = false;
}

frmMain::~frmMain() { delete ui; }

bool frmMain::eventFilter(QObject *watched, QEvent *event) {
    if (event->type() == QEvent::MouseButtonDblClick) {
        if (watched == ui->widgetTitle) {
            on_btnMenu_Max_clicked();
            return true;
        }
    }

    // qDebug() << "watched " << watched << ", event " << event->type();

    if (widget && watched == widget) {
        int type = event->type();
        if (type == QEvent::WindowStateChange) {
            //解决mac系统上无边框最小化失效的bug
#ifdef Q_OS_MACOS
            if (widget->windowState() & Qt::WindowMinimized) {
                isMin = true;
            } else {
                if (isMin) {
                    //设置无边框属性
                    widget->setWindowFlags(flags | Qt::FramelessWindowHint);
                    widget->setVisible(true);
                    isMin = false;
                }
            }
#endif
        } else if (type == QEvent::Resize) {
            //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内
            int width = widget->width();
            int height = widget->height();

            //左侧描点区域
            pressedRect[0] = QRect(0, padding, padding, height - padding * 2);
            //右侧描点区域
            pressedRect[1] =
                QRect(width - padding, padding, padding, height - padding * 2);
            //上侧描点区域
            pressedRect[2] = QRect(padding, 0, width - padding * 2, padding);
            //下侧描点区域
            pressedRect[3] =
                QRect(padding, height - padding, width - padding * 2, padding);

            //左上角描点区域
            pressedRect[4] = QRect(0, 0, padding, padding);
            //右上角描点区域
            pressedRect[5] = QRect(width - padding, 0, padding, padding);
            //左下角描点区域
            pressedRect[6] = QRect(0, height - padding, padding, padding);
            //右下角描点区域
            pressedRect[7] =
                QRect(width - padding, height - padding, padding, padding);
        } else if (type == QEvent::HoverMove) {
            //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别
            QHoverEvent *hoverEvent = (QHoverEvent *)event;
            QPoint point = hoverEvent->pos();
            if (resizeEnable) {
                if (pressedRect.at(0).contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (pressedRect.at(1).contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (pressedRect.at(2).contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (pressedRect.at(3).contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (pressedRect.at(4).contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else if (pressedRect.at(5).contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (pressedRect.at(6).contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (pressedRect.at(7).contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else {
                    widget->setCursor(Qt::ArrowCursor);
                }
            }

            //根据当前鼠标位置,计算XY轴移动了多少
            int offsetX = point.x() - mousePoint.x();
            int offsetY = point.y() - mousePoint.y();

            //根据按下处的位置判断是否是移动控件还是拉伸控件
            if (moveEnable && mousePressed) {
                widget->move(widget->x() + offsetX, widget->y() + offsetY);
            }

            if (resizeEnable) {
                int rectX = mouseRect.x();
                int rectY = mouseRect.y();
                int rectW = mouseRect.width();
                int rectH = mouseRect.height();

                if (pressedArea.at(0)) {
                    int resizeW = widget->width() - offsetX;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, rectY,
                                            resizeW, rectH);
                    }
                } else if (pressedArea.at(1)) {
                    widget->setGeometry(rectX, rectY, rectW + offsetX, rectH);
                } else if (pressedArea.at(2)) {
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(rectX, widget->y() + offsetY, rectW,
                                            resizeH);
                    }
                } else if (pressedArea.at(3)) {
                    widget->setGeometry(rectX, rectY, rectW, rectH + offsetY);
                } else if (pressedArea.at(4)) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(),
                                            resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY,
                                            resizeW, resizeH);
                    }
                } else if (pressedArea.at(5)) {
                    int resizeW = rectW + offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY,
                                            resizeW, resizeH);
                    }
                } else if (pressedArea.at(6)) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = rectH + offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(),
                                            resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y(), resizeW,
                                            resizeH);
                    }
                } else if (pressedArea.at(7)) {
                    int resizeW = rectW + offsetX;
                    int resizeH = rectH + offsetY;
                    widget->setGeometry(widget->x(), widget->y(), resizeW,
                                        resizeH);
                }
            }
        } else if (type == QEvent::MouseButtonPress) {
            //记住鼠标按下的坐标+窗体区域
            QMouseEvent *mouseEvent = (QMouseEvent *)event;
            mousePoint = mouseEvent->pos();
            mouseRect = widget->geometry();

            //判断按下的手柄的区域位置
            if (pressedRect.at(0).contains(mousePoint)) {
                pressedArea[0] = true;
            } else if (pressedRect.at(1).contains(mousePoint)) {
                pressedArea[1] = true;
            } else if (pressedRect.at(2).contains(mousePoint)) {
                pressedArea[2] = true;
            } else if (pressedRect.at(3).contains(mousePoint)) {
                pressedArea[3] = true;
            } else if (pressedRect.at(4).contains(mousePoint)) {
                pressedArea[4] = true;
            } else if (pressedRect.at(5).contains(mousePoint)) {
                pressedArea[5] = true;
            } else if (pressedRect.at(6).contains(mousePoint)) {
                pressedArea[6] = true;
            } else if (pressedRect.at(7).contains(mousePoint)) {
                pressedArea[7] = true;
            } else {
                mousePressed = true;
            }
        } else if (type == QEvent::MouseMove) {
            //改成用HoverMove识别
        } else if (type == QEvent::MouseButtonRelease) {
            //恢复所有
            widget->setCursor(Qt::ArrowCursor);
            mousePressed = false;
            for (int i = 0; i < 8; ++i) {
                pressedArea[i] = false;
            }
        }
    }

    return QWidget::eventFilter(watched, event);
}

void frmMain::loadDevices() {
    ui->treeWidgetCamera->setHeaderHidden(true);
    QTreeWidgetItem *computer = new QTreeWidgetItem(ui->treeWidgetCamera);
    computer->setIcon(0, QIcon(":/image/computer.png"));
    computer->setText(0, "此电脑");
    computer->setExpanded(true);
    ui->treeWidgetCamera->addTopLevelItem(computer);

    auto cameras = QMediaDevices::videoInputs();
    for (const QCameraDevice &camera : cameras) {
        QTreeWidgetItem *item = new QTreeWidgetItem(computer);
        item->setIcon(0, QIcon(":/image/camera.png"));
        item->setText(0, camera.description());
        item->setData(0, Qt::UserRole, QVariant::fromValue(camera));

        qDebug() << camera.description() << ", id: " << camera.id();
        auto videoFormats = camera.videoFormats();
        for (auto &format : videoFormats) {
            qDebug() << " - resolution " << format.resolution()
                     << ", frame rate [" << format.minFrameRate() << ", "
                     << format.maxFrameRate() << "], format "
                     << format.pixelFormat();
        }
    }

    //    ui->comboBoxAudioDevice->addItem(tr("Default"), QVariant(QString()));
    for (auto device : QMediaDevices::audioInputs()) {
        auto name = device.description();
        ui->comboBoxAudioDevice->addItem(name, QVariant::fromValue(device));
    }

    ui->comboBoxQuality->addItem("很低", int(QImageCapture::VeryLowQuality));
    ui->comboBoxQuality->addItem("低", int(QImageCapture::LowQuality));
    ui->comboBoxQuality->addItem("正常", int(QImageCapture::NormalQuality));
    ui->comboBoxQuality->addItem("高", int(QImageCapture::HighQuality));
    ui->comboBoxQuality->addItem("很高", int(QImageCapture::VeryHighQuality));
    ui->comboBoxQuality->setCurrentIndex(2);
}

void frmMain::initResizeMembers() {
    //设置鼠标追踪为真，不然只会在鼠标按下时才会触发鼠标移动事件
    this->setMouseTracking(true);
    //设置悬停为真,必须设置这个,不然当父窗体里边还有子窗体全部遮挡了识别不到MouseMove,需要识别HoverMove
    this->setAttribute(Qt::WA_Hover, true);
    // setWindowFlags(Qt::FramelessWindowHint);// 设置窗口为无边框窗口
    // 只有安装了事件过滤器，才会进入到eventFilter，很重要！！
    installEventFilter(this);

    padding = 8;
    moveEnable = true;
    resizeEnable = true;
    widget = this;

    mousePressed = false;
    mousePoint = QPoint(0, 0);
    mouseRect = QRect(0, 0, 0, 0);

    for (int i = 0; i < 8; ++i) {
        pressedArea << false;
        pressedRect << QRect(0, 0, 0, 0);
    }
}

void frmMain::loadCameraProperties(const QCameraDevice &camera) {
    ui->comboBoxPixFormat->clear();
    cameraFormats = camera.videoFormats();

    QSet<QVideoFrameFormat::PixelFormat> pixFormats;
    for (auto &format : cameraFormats) {
        if (!pixFormats.contains(format.pixelFormat())) {
            ui->comboBoxPixFormat->addItem(
                QVideoFrameFormat::pixelFormatToString(format.pixelFormat()),
                format.pixelFormat());
            pixFormats.insert(format.pixelFormat());
        }
    }

    ui->comboBoxPixFormat->setCurrentIndex(0);
}

void frmMain::setCamera(const QCameraDevice &cameraDevice) {
    curCamera.reset(new QCamera(cameraDevice));
    captureSession.setCamera(curCamera.data());

    if (!mediaRecorder) {
        mediaRecorder.reset(new QMediaRecorder);
        captureSession.setRecorder(mediaRecorder.data());
    }

    if (!imgCapture) {
        imgCapture.reset(new QImageCapture);
        imgCapture->setFileFormat(QImageCapture::PNG);

        captureSession.setImageCapture(imgCapture.get());

        //        connect(imgCapture.get(), &QImageCapture::imageCaptured,
        //                [this](int id, const QImage &preview) {
        //                    qDebug()
        //                        << "QImageCapture::imageCaptured" << id <<
        //                        preview;
        //                    //
        //                    ui->label->setPixmap(QPixmap::fromImage(preview.scaled(ui->label->size(),Qt::KeepAspectRatio)));
        //                });
    }

    captureSession.setVideoOutput(ui->widgetVideo);
}

void frmMain::setCameraSelectedFormat() {
    QCameraFormat format =
        ui->comboBoxFps->currentData().value<QCameraFormat>();
    curCamera->setCameraFormat(format);
}

void frmMain::startCamera() {
    if (curCamera)
        curCamera->start();
    else {
        QUIHelper::showMessageBoxError("没有摄像头对象");
    }
}

void frmMain::stopCamera() {
    if (curCamera)
        curCamera->stop();
    else {
        QUIHelper::showMessageBoxError("没有摄像头对象");
    }
}

void frmMain::initForm() {
    //设置无边框
    QUIHelper::setFramelessForm(this);
    //设置图标
    QPixmap pixmap(":/image/video.png");
    ui->labIco->setPixmap(pixmap.scaled(QSize(35, 35)));
    IconHelper::setIcon(ui->btnMenu_Min, 0xf068);
    IconHelper::setIcon(ui->btnMenu_Max, 0xf067);
    IconHelper::setIcon(ui->btnMenu_Close, 0xf00d);

    // ui->widgetMenu->setVisible(false);
    ui->widgetTitle->installEventFilter(this);
    ui->widgetTitle->setProperty("form", "title");
    ui->widgetTop->setProperty("nav", "top");

    // ui->stackedWidget->installEventFilter(this);

    QFont font;
    font.setPixelSize(25);
    ui->labTitle->setFont(font);
    ui->labTitle->setText("摄像头录像机");
    this->setWindowTitle(ui->labTitle->text());

    //单独设置指示器大小
    int addWidth = 20;
    int addHeight = 10;
    int rbtnWidth = 15;
    int ckWidth = 13;
    int scrWidth = 12;
    int borderWidth = 3;

    QStringList qss;
    qss << QString("QComboBox::drop-down,QDateEdit::drop-down,QTimeEdit::drop-"
                   "down,QDateTimeEdit::drop-down{width:%1px;}")
               .arg(addWidth);
    qss << QString(
               "QComboBox::down-arrow,QDateEdit[calendarPopup=\"true\"]::"
               "down-arrow,QTimeEdit[calendarPopup=\"true\"]::down-arrow,"
               "QDateTimeEdit[calendarPopup=\"true\"]::down-arrow{width:%1px;"
               "height:%1px;right:2px;}")
               .arg(addHeight);
    qss << QString("QRadioButton::indicator{width:%1px;height:%1px;}")
               .arg(rbtnWidth);
    qss << QString("QCheckBox::indicator,QGroupBox::indicator,QTreeWidget::"
                   "indicator,QListWidget::indicator{width:%1px;height:%1px;}")
               .arg(ckWidth);
    qss << QString("QScrollBar:horizontal{min-height:%1px;border-radius:%2px;}"
                   "QScrollBar::handle:horizontal{border-radius:%2px;}"
                   "QScrollBar:vertical{min-width:%1px;border-radius:%2px;}"
                   "QScrollBar::handle:vertical{border-radius:%2px;}")
               .arg(scrWidth)
               .arg(scrWidth / 2);
    qss << QString("QWidget#widget_top>QToolButton:pressed,QWidget#widget_top>"
                   "QToolButton:hover,"
                   "QWidget#widget_top>QToolButton:checked,QWidget#widget_top>"
                   "QLabel:hover{"
                   "border-width:0px 0px %1px 0px;}")
               .arg(borderWidth);
    qss << QString("QWidget#widgetleft>QPushButton:checked,QWidget#widgetleft>"
                   "QToolButton:checked,"
                   "QWidget#widgetleft>QPushButton:pressed,QWidget#widgetleft>"
                   "QToolButton:pressed{"
                   "border-width:0px 0px 0px %1px;}")
               .arg(borderWidth);
    this->setStyleSheet(qss.join(""));

    QSize icoSize(32, 32);
    int icoWidth = 85;

    //设置顶部导航按钮
    QList<QToolButton *> tbtns = ui->widgetTop->findChildren<QToolButton *>();
    foreach (QToolButton *btn, tbtns) {
        btn->setIconSize(icoSize);
        btn->setMinimumWidth(icoWidth);
        btn->setCheckable(true);
        connect(btn, SIGNAL(clicked()), this, SLOT(buttonClick()));
    }

    ui->btnMain->click();
}

void frmMain::buttonClick() {
    QToolButton *b = (QToolButton *)sender();
    QString name = b->text();

    QList<QToolButton *> tbtns = ui->widgetTop->findChildren<QToolButton *>();
    foreach (QToolButton *btn, tbtns) { btn->setChecked(btn == b); }

    if (name == "主界面") {
        ui->stackedWidget->setCurrentIndex(0);
    } else if (name == "系统设置") {
        ui->stackedWidget->setCurrentIndex(1);
    } else if (name == "调试帮助") {
        ui->stackedWidget->setCurrentIndex(2);
    } else if (name == "用户退出") {
        exit(0);
    }
}

void frmMain::on_btnMenu_Min_clicked() { showMinimized(); }

void frmMain::on_btnMenu_Max_clicked() {
    static bool max = false;
    static QRect location = this->geometry();

    if (max) {
        this->setGeometry(location);
    } else {
        location = this->geometry();
        this->setGeometry(QUIHelper::getScreenRect());
    }

    this->setProperty("canMove", max);
    max = !max;
}

void frmMain::on_btnMenu_Close_clicked() { close(); }

void frmMain::on_toolButtonCapture_clicked() {
    // https://blog.csdn.net/qq_63986699/article/details/130440700
    // https://doc.qt.io/qt-6.5/qimagecapture.html
    if (!curCamera) {
        QUIHelper::showMessageBoxError("没有打开摄像头");
        return;
    }

    auto quality =
        ui->comboBoxQuality->currentData().value<QImageCapture::Quality>();
    imgCapture->setQuality(quality);

    QDateTime currentDateTime = QDateTime::currentDateTime();
    QString fileName = currentDateTime.toString("yyyy-MM-dd_hhmmss");
    imgCapture->captureToFile(fileName);

    qDebug() << "已截图" << fileName;
}

void frmMain::on_toolButtonApply_clicked() {
    if (!curCamera) {
        QUIHelper::showMessageBoxError("没有打开摄像头");
        return;
    }

    stopCamera();
    setCameraSelectedFormat();
    startCamera();
}

void frmMain::on_toolButtonRecord_clicked() {
    if (!curCamera) {
        QUIHelper::showMessageBoxError("没有打开摄像头");
        return;
    }
    if (!recording) {
        recording = true;

        if (ui->checkBoxAudio->isChecked()) {
            auto audioDevice =
                ui->comboBoxAudioDevice->currentData().value<QAudioDevice>();
            audioInput.reset(new QAudioInput(audioDevice));
            captureSession.setAudioInput(audioInput.get());
        } else {
            captureSession.setAudioInput(nullptr);
        }

        QMediaFormat format;
        format.setAudioCodec(QMediaFormat::AudioCodec::Unspecified); // aac
        format.setVideoCodec(QMediaFormat::VideoCodec::Unspecified); // h264
        mediaRecorder->setMediaFormat(format);

        // 值一样，控件共用
        auto quality =
            ui->comboBoxQuality->currentData().value<QMediaRecorder::Quality>();
        mediaRecorder->setQuality(quality);
        QDateTime currentDateTime = QDateTime::currentDateTime();
        QString fileName = currentDateTime.toString("yyyy-MM-dd_hhmmss");
        mediaRecorder->setOutputLocation(QUrl::fromLocalFile(fileName));
        mediaRecorder->setEncodingMode(QMediaRecorder::ConstantQualityEncoding);
        mediaRecorder->record();
        ui->toolButtonRecord->setText("停止");
    } else {
        recording = false;

        mediaRecorder->stop();
        ui->toolButtonRecord->setText("录像");
    }
}

void frmMain::on_treeWidgetCamera_itemDoubleClicked(QTreeWidgetItem *item,
                                                    int column) {

    // https://blog.csdn.net/u011442415/article/details/129370856

    auto cameraDevice = item->data(0, Qt::UserRole).value<QCameraDevice>();
    qDebug() << "激活摄像头" << cameraDevice.description() << ", id "
             << cameraDevice.id();

    // 加载摄像头支持的参数到控件
    loadCameraProperties(cameraDevice);
    setCamera(cameraDevice);

    auto videoFormats = cameraDevice.videoFormats();
    for (auto &format : videoFormats) {
        qDebug() << "resolution " << format.resolution() << ", frame rate ["
                 << format.minFrameRate() << ", " << format.maxFrameRate()
                 << "], format " << format.pixelFormat();
        //                if (format.maxFrameRate() == 30 &&
        //                    format.pixelFormat() ==
        //                    QVideoFrameFormat::Format_Jpeg &&
        //                    format.resolution() == QSize(1920, 1080))
        //                    { curCamera->setCameraFormat(format);
        //                    qDebug() << "set format resolution " <<
        //                    format.resolution()
        //                             << ", frame rate [" <<
        //                             format.minFrameRate()
        //                             << ", " << format.maxFrameRate()
        //                             << "], format "
        //                             << format.pixelFormat();
        //                }
    }

    // https://doc.qt.io/qt-6/qcameraformat.html
    startCamera();
}

void frmMain::on_comboBoxPixFormat_currentIndexChanged(int index) {
    if (index == -1)
        return;

    qDebug() << "selected pix format "
             << ui->comboBoxPixFormat->itemText(index);
    QVideoFrameFormat::PixelFormat curFmt =
        ui->comboBoxPixFormat->itemData(index)
            .value<QVideoFrameFormat::PixelFormat>();

    ui->comboBoxResolution->clear();

    QSet<QSize> reso;
    for (auto &format : cameraFormats) {
        if (curFmt == format.pixelFormat() &&
            !reso.contains(format.resolution())) {
            QString txt = QString("%1 x %2")
                              .arg(format.resolution().width())
                              .arg(format.resolution().height());
            ui->comboBoxResolution->addItem(txt, format.resolution());
            reso.insert(format.resolution());
        }
    }

    ui->comboBoxResolution->setCurrentIndex(0);
}

void frmMain::on_comboBoxResolution_currentIndexChanged(int index) {
    if (index == -1)
        return;

    qDebug() << "selected resolution "
             << ui->comboBoxResolution->itemText(index);
    QSize curReso = (QSize)ui->comboBoxResolution->itemData(index).toSize();

    ui->comboBoxFps->clear();

    QVideoFrameFormat::PixelFormat curFmt =
        (QVideoFrameFormat::PixelFormat)ui->comboBoxPixFormat->currentData()
            .toInt();

    QSet<int> fps;

    for (auto &format : cameraFormats) {
        if (curFmt == format.pixelFormat() && curReso == format.resolution()) {
            if (!fps.contains(format.minFrameRate())) {
                ui->comboBoxFps->addItem(QString::number(format.minFrameRate()),
                                         QVariant::fromValue(format));
                fps.insert(format.minFrameRate());
            }
        }
    }

    ui->comboBoxFps->setCurrentIndex(0);
}
